DllTest - sample code for Python
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This sample code will work with versions of Python 2.6 and higher.

Note - for Cython Sample code please look in the Cython sub-folder.

The source files in the project are:

File name       Description
dlltest.py      main source code file for Python containing protection functions
dris.py         contains useful constants and functions that implement the DRIS structure.

The sample code works by calling a Python extension that communicates with the dongle.
The Python extension is located in the Modules/Python directory of the SDK. You need to
use the extension that matches the version of Python you are using. For each version of
Python we have extensions for each supported Operating System:

dpwin32py.pyd - Windows 32-bit
dpwin64py.pyd - Windows 64-bit
dplin32py.so  - Linux 32-bit
dplin64py.so  - Linux 64-bit
dpmac64py.so  - MacOS 64-bit

Please contact Microcosm if you require a Python module for a different version to those we
provide and we will generate it for you.

The sample code (dris.py) will automatically work out the OS and bitness and load the
appropriate extension for you.

The sample code contains 10 functions which demonstrate some of the functionality offered by
the Dinkey Pro/FD system.

Code marked with !!!! are places where you should customise the sample code so
that it will work properly:

* Change the MY_SDSN value to the value of your SDSN
* Change the MY_PRODCODE value to the Product Code in the dongle
* Change the MyAlgorithm code (if you call a user algorithm)
* Change the CryptDRIS code (if you encrypt the DRIS)
* Change the CryptApiData (if you encrypt Data you send to our API)
* Change the MyRWAlgorithm code (if encrypt Data you send to our API using the R/W algorithm)

You should use DinkeyAdd to protect the Python extension(s) (e.g. dpmac64py.pyd etc...) and
not your Python code directly. Because your program is linked to the protected extension
then it is protected. 

How To Protect The Python Sample Code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use DinkeyAdd (using the API Method of protection) to protect the Dinkey Pro/FD Python
extensions we provide for the version of Python you want to support. (If you know which
Operating Systems you will support then you can just protect these extensions. If you are
not sure then just protect them all).

For example, if you were just distributing on Windows with 64-bit python 3.x then you should
specify the extension: Modules/Python/x86-amd64/3.x/dpwin64py.pyd as the input pathname and
the location of your python code as the output pathname. Do this for each extension you want
to protect. In our example you will have the following in your sample code folder:

dlltest.py
dris.py
dpwin64py.pyd         [output from DinkeyAdd]

To test everything is working run the sample code with and without the dongle attached.

Note - the version of the extension you provide must match the version of Python that is
being run. If not you will get an error. In our experience this is different for each OS:
Windows: gives a sensible error message saying that the version of the extension is different.
Linux: We actually found that any 2.x extension could run on any 2.y Python (similar for 3.x).
       Otherwise you get an error about PyInit_dpxxxx or init_dpxxx not being found in the
       extension.
Mac: you get the very unhelpful error "PyThreadState_Get: no current thread"


How To Add Protection to your Python Code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1) Modify your python code to check the dongle - you can base your protection check function
   on those from dlltest.py in the sample code. The sample code in dlltest.py contains 10
   functions. You will not need to use all these functions in your code. Just use which ever
   functions are most appropriate and customise in your own way. The sample code is just a
   guide. You can implement the protection in a stronger way using the techniques described in
   the "Increasing your Protection" chapter of the manual.
   
2) You should import dris.py into your python code. 

3) You will probably want to modify some of the error messages in dris.py with your own.
   However, you should still display or log any error codes returned by the protection check
   otherwise you will not be able to identify the cause of the error. You can look up error
   codes in our knowledge base: www.microcosm.com/kb.

4) Protect the relevant Python extensions just like in the step above for the sample code.

5) The protected Python extensions (for your version of Python) should be in the same
   directory as the dris module. 
   
6) Compile your Python code (py files) to pyc files for distribution (see instructions below).

Algorithms in Python
~~~~~~~~~~~~~~~~~~~~

Python implements modulo and integer division differently to other programming languages
like C, when it is applied to negative integers. Also, when you perform arithmetic in Python
it can expand the size of the variable to accommodate the size of the result. When the
algorithms are executed in the dongle they assume the output and all the inputs are signed
integers. To ensure that the algorithms are implemented in Python so that they match the
output of the dongle you need to be aware of the following:

* use the mod function defined in dlltest.py instead of the operator %
* use the div function defined in dlltest.py instead of the integer operator //
* apply the function dris.make_signed32bit to the value of the algorithm to ensure the output
  is a 32-bit signed value

Level of Security
~~~~~~~~~~~~~~~~~
You should not distribute your py code as this editable and therefore not secure (an end
user could edit the file and remove all the protection checks). You should distribute the
compiled pyc or pyo files and the protected extensions. These files contain byte-code.
You can do this by compiling your python code like this:

python -m compile_all -b ./                 [.\ for windows]

This will generate the pyc files in the same directory as the py files (rather than the
__pycache__ directory) and ensure that they will work in a pyc-only distribution.

However, although byte-code is not readable like source code, it is still possible to use
tools to decompile it back to source code. This will not be as easy to read as your source
code but a hacker might be able to work out what your code is doing, remove the protection
checks and re-compile. Also - it is possible to use tools that modify the byte-code directly.

One way around this is to write your code (or the most sensitive parts of your code) in Cython
(see Cython sample code) or in pure C (see C/C++ sample code). This is recommended if you have
the skills to do it. Another technique is to use a Python obfuscator so that the bytecode is
much more difficult to understand.

Another tool you could look at is Nuitka (nuitka.net) which converts Python code to C and then
compiles it. 

Another possible hack is to use the Python interpreter to replace the protection check function
with a function that the hacker has written himself. You can combat this hack by using a user
algorithm or (not quite so good) reading data from the dongle data area. This works because it
means that the hacker would have to work out the algorithm (data) to defeat the protection.
Another solution is write some of your code in pure C and call it from Python.

Note - you may be tempted to not modify your source code and instead use py2exe and the Shell
Method to protect the resulting executable. However, this is not secure as the "compiled"
executable file is just a loader. Your compiled Python code is also included in the library.zip
file, so someone could just extract these files and use them. Therefore you should not use this
method but to use the API method with the source code instead. You can then use py2exe
(without the Shell method) if you prefer to distribute your software this way.

This is also the case for any other Python-to-executable software unless it specifically says
that your compiled Python code is encrypted. We have not seen this feature in any Python-to-exe
converter.
